package Models

import (
	"fmt"
	"gPublic/Common"
	"reflect"
	"strconv"
	"strings"
)

type MediaType string

const (
	MediaTypeApplication MediaType = "application"
	MediaTypeAudio       MediaType = "audio"
	MediaTypeVideo       MediaType = "video"
	MediaTypeImage       MediaType = "image"
	MediaTypePoly        MediaType = "poly"
	MediaTypeTencent     MediaType = "tencent"
)

type GZAnnexFile struct {
	Id               int64     `json:"id" gorm:"primary_key"`
	Title            string    `json:"title" gorm:"size:255"`
	CateId           string    `json:"cate_id" gorm:"type:int;size:4"`
	FilePath         string    `json:"file_path" gorm:"size:255"`
	EncryPath        string    `json:"encry_path" gorm:"column:encry_path; size:255"`
	Secret           string    `json:"secret" gorm:"size:32"`
	SlicingPath      string    `json:"slicing_path" gorm:"size:1"`
	FileType         MediaType `json:"file_type" gorm:"type:enum('application','audio','video','image','poly','tencent');comment:'文件类型 application,audio,video:视频,image:图片,poly:保利威视视频,tencent:腾讯视频'"`
	FileSuffix       string    `json:"file_suffix" gorm:"size:20"`
	FileSize         int64     `json:"file_size"`
	Duration         int64     `json:"duration"`
	Width            int64     `json:"width"`
	Height           int64     `json:"height"`
	Avthumb          string    `json:"avthumb" gorm:"size:255"`
	FileInfo         string    `json:"file_info" gorm:"type:text"`
	ConvertInfo      string    `json:"convert_info" gorm:"type:text"`
	ConvertEncryInfo string    `json:"convert_encry_info" gorm:"type:text"`
	Convert          int32     `json:"convert"`
	Hash             string    `json:"hash" gorm:"size:255"`
	Closed           int64     `json:"closed"`
	CreatedAt        int64     `json:"create_time"`
	UpdatedAt        int64     `json:"update_time"`
}

func (GZAnnexFile) TableName() string {
	return "gz_annex_file"
}

var fileTP = []string{"jpg", "jpeg", "png"}
var annexFields = []string{"FilePath", "EncryPath", "SlicingPath"}

type ResFile struct {
	Id          int64
	FileType    MediaType
	FileInfo    string
	FileSuffix  string
	Secret      string
	Width       int64
	Height      int64
	FilePath    string
	SlicingPath string
	EncryPath   string
}

func GetAuthFile(logoId int, source int) (ResFile, error) {

	// 结构体无法遍历
	cfg := ResFile{}
	err := DB.Table("gz_annex_file").Where("id = ?", logoId).Find(&cfg).Error

	if err == nil {
		hasIn := Common.IsInArray(cfg.FileSuffix, fileTP)

		if cfg.FileType == "image" && hasIn {
			var swidth int64
			if source < 1 {
				swidth = 750
			} else {
				swidth = 1180
			}
			// 重构图片宽度 【 *** 结构体无法遍历 因此用通过反射来处理 *** 】
			if cfg.Width > swidth {
				newCfg, rv := reflectOneDStructPrune(cfg, swidth)
				// 打印修改后数据
				fmt.Printf("原始结构体 -===== new： %v\n", newCfg)
				fmt.Printf("反射对象： %v\n", rv.Interface())
				return newCfg, nil
			}

			// 视频
		} else if cfg.FileType == "video" {

			if cfg.Secret != "" {
				reflectOneDStructVideo(cfg)
				newCfg, _ := reflectOneDStructVideo(cfg)

				// fmt.Printf("video 原始结构体 -===== new： %#v\n", newCfg)
				// fmt.Printf("video 反射对象： %#v\n", rv.Interface())
				return newCfg, nil
			}

		}

		// 其他
	} else {
		reflectOneDStruct(cfg)
		cfg.FilePath = ""
	}

	return cfg, err
}

// video 内容
func reflectOneDStructVideo(cfg ResFile) (ResFile, reflect.Value) {
	rv := reflect.ValueOf(&cfg).Elem()

	if rv.Kind() == reflect.Struct {
		for i := 0; i < rv.NumField(); i++ {
			fieldName := rv.Type().Field(i).Name
			fieldValue := rv.Field(i)
			fieldType := fieldValue.Type()
			kind := fieldType.Kind()

			videoHasIn := Common.IsInArray(fieldName, annexFields)

			if videoHasIn && (fieldName == "SlicingPath" || fieldName == "EncryPath") {
				//  判断字符串中是否包含子串 -  转小写
				if strings.Contains(strings.ToLower(".mp4"), fieldValue.String()) {
					if kind == reflect.String && fieldValue.String() != "" {
						url, _ := Common.DownloadUrl(fieldValue.String(), 3600)
						if fieldValue.CanSet() {
							fieldValue.SetString(Common.StrToXHex(url))
							// fieldValue.SetString(url)
						} else {
							fmt.Println("字段不可更改")
						}
					}
				} else {
					if kind == reflect.String && fieldValue.String() != "" {
						url := Common.GetVideoAuth(fieldValue.String(), 3600)
						url += "&id=" + strconv.FormatInt(cfg.Id, 10)
						if fieldValue.CanSet() {
							fieldValue.SetString(Common.StrToXHex(url))
						} else {
							fmt.Println("--------------------- 字段不可更改")
						}
					}
				}

				// 随意写的
			} else if videoHasIn && fieldName == "file_path" {
				if strings.Contains(strings.ToLower(".mp4"), fieldValue.String()) {
					if kind == reflect.String && fieldValue.String() != "" {
						url, _ := Common.DownloadUrl(fieldValue.String(), 3600)
						if fieldValue.CanSet() {
							fieldValue.SetString(Common.StrToXHex(url))
							// fieldValue.SetString(url)
						} else {
							fmt.Println("字段不可更改")
						}
					}
				}
			}
		}
	}
	return cfg, rv
}

func reflectOneDStruct(cfg ResFile) (ResFile, reflect.Value) {
	rv := reflect.ValueOf(&cfg).Elem()
	if rv.Kind() == reflect.Struct {
		for i := 0; i < rv.NumField(); i++ {
			fieldName := rv.Type().Field(i).Name
			fieldValue := rv.Field(i)
			fieldType := fieldValue.Type()
			kind := fieldType.Kind()
			if fieldName == "FilePath" {
				hasField := Common.IsInArray(fieldName, annexFields)
				if hasField && kind == reflect.String && fieldValue.String() != "" {
					url, _ := Common.DownloadUrl(fieldValue.String(), 3600)
					if fieldValue.CanSet() {
						fieldValue.SetString(url)
					} else {
						fmt.Println("字段不可更改")
					}
				}
			}
		}
	}
	return cfg, rv
}

// 反射结构体 修改其中的值 - 修改图片大小
func reflectOneDStructPrune(cfg ResFile, swidth int64) (ResFile, reflect.Value) {

	rv := reflect.ValueOf(&cfg).Elem() // 注意用 引入的方法“&” 和 Elem（） 才支持修改里面的数据 ******

	if rv.Kind() == reflect.Struct {
		// 根据结构体反射的长度，遍历结构体的所有字段 【这里是反射后的结构体】
		for i := 0; i < rv.NumField(); i++ {
			// 获取字段名和值
			fieldName := rv.Type().Field(i).Name // 获取字段名称
			// fieldValue := rv.Field(i).Interface() // 获取对应 i【字段】的值  , Interface() 值是一个空接口类型interface{}，该值会丢失具体的类型信息。如果需要获取该值的真实类型，可以结合使用Type()方法和类型断言来实现。
			fieldValue := rv.Field(i) // 值  返回的是一个 int64 类型的值
			// fmt.Printf("-----tttttttttttt %T\n", fieldValue)

			fieldType := fieldValue.Type() // 类型
			kind := fieldType.Kind()       // 类型的种类 - 原始类型 通过这个kind 就可以获取到 是什么类型 如这里的就是 reflect.String类型，这里的类型就是原始类型 即属于reflect的
			// fmt.Printf("--111222222  %s: %v : %v : %v\n", fieldName, kind, fieldValue, fieldType) // FilePath: string : image_0.33454006681575565.png : string
			if fieldName == "FilePath" {
				// fmt.Printf("-- %v %T\n", fieldValue, fieldValue)
				hasField := Common.IsInArray(fieldName, annexFields)

				// fmt.Printf("-----hasField %v\n", hasField)
				// 反射值 不能直接和字符串笔记 fieldValue != ""，需要首先将反射值转换回其原始类型
				// 我们首先检查 kind 的类型是否是 reflect.String，然后使用 value.String() 获取字符串值并进行比较。这样就避免了类型不匹配的错误。
				if hasField && kind == reflect.String && fieldValue.String() != "" {
					// 	// 取新的图片
					url := Common.QiNiuImageAutoThumb(fieldValue.String(), swidth, 3600) // fieldValue = image_0.33454006681575565.png
					// fmt.Printf("-----urls %v\n", url)

					// 确保 fieldValue 是可更改的。若不可更改，您需要使用 Elem() 方法获取可更改的值
					// Elem() 方法获取了指向可更改值的指针
					// 根据字段类型进行操作
					if fieldValue.CanSet() { // fieldValue.CanSet() 是 reflect.Value 类型的方法，用于检查该值是否可以被修改。如果返回 true，表示该值是可修改的；如果返回 false，表示该值不可修改。
						// 确保字段可更改
						if fieldValue.Kind() == reflect.String {
							// 检查字段类型是否为 string
							fieldValue.SetString(url) // 设置新的 int 值
						} else {
							fmt.Println("字段类型不是 String，无法重新赋值")
						}
					} else {
						fmt.Println("字段不可更改")
					}
				}
			}
			// fmt.Printf("-- %s: %v\n", fieldName, fieldValue)
			// -- Id: 4798
			// -- FilePath: image_0.33454006681575565.png
			// -- EncryPath:
			// -- Secret:
			// -- SlicingPath:
			// -- FileType: image
			// -- FileSuffix: png
			// -- Width: 750
			// -- Height: 158
			// -- FileInfo: {"size":14880,"format":"png","width":750,"height":158,"colorModel":"nrgba"}
		}
	}
	return cfg, rv
}
